`define YSYX210152_XLEN 16

module FIBSlave(
    // global input
    input VFIBi_CLK,
    input VFIBi_ARST,
    // master request
    input VFIBi_V,
    input[7:0] VFIBi_ID,
    input[7:0] VFIBi_CMD,
    input[3:0] VFIBi_BURST,
    input[3:0] VFIBi_SIZE,
    input[`XLEN-1:0] VFIBi_ADDR,
    input[`XLEN-1:0] VFIBi_DATA,
    // slave reply
    output VFIBo_FULL,
    output VFIBo_V,
    output[7:0] VFIBo_ID,
    output[7:0] VFIBo_RPL,
    output[`XLEN-1:0] VFIBo_DATA
);

parameter NOOP = 8'h00;
parameter SIGR = 8'h01;
parameter SIGW = 8'h02;
parameter SEQR = 8'h03;
parameter SEQW = 8'h04;
parameter SEQE = 8'h14;
parameter TRDY = 8'h10;
parameter SEQ = 8'h14;
parameter Ready = 3'h0;
parameter SRead = 3'h1;
parameter SWrite = 3'h2;
parameter BRead = 3'h3;
parameter BWrite = 3'h4;
parameter NULL = `XLEN'h0;


    reg VFIBi_V_reg;
    reg[7:0] VFIBi_ID_reg;
    reg[7:0] VFIBi_CMD_reg;
    reg[3:0] VFIBi_BURST_reg;
    reg[3:0] VFIBi_SIZE_reg;
    reg[`XLEN-1:0] VFIBi_ADDR_reg;
    reg[`XLEN-1:0] VFIBi_DATA_reg;
    reg VFIBo_V_reg;
    reg[`XLEN-1:0] VFIBo_DATA_reg;
    reg[2:0] state;
    reg[2:0] s_next;
    reg[15:0] count;
    reg[`XLEN-1:0] mem[0:(1<<`XLEN)-1];
    wire bw_wen;

    assign VFIBo_FULL = state != Ready;
    assign VFIBo_V = VFIBo_V_reg;
    assign VFIBo_ID = VFIBo_V ? VFIBi_ID_reg : 8'h0;
    assign VFIBo_RPL = VFIBo_V ? (VFIBi_CMD_reg == SEQR || VFIBi_CMD_reg == SEQW ? (count == 16'b0 ? TRDY : SEQ) : TRDY) : NOOP;
    assign VFIBo_DATA = VFIBo_V ? (VFIBi_CMD_reg == SIGR || VFIBi_CMD_reg == SEQR) ? VFIBo_DATA_reg : NULL : NULL;

    always @(posedge VFIBi_CLK)
        VFIBi_V_reg <= VFIBi_V;

    always @(posedge VFIBi_CLK or negedge VFIBi_ARST) begin
        if (~VFIBi_ARST) begin
            VFIBi_ID_reg <= 8'h0;
            VFIBi_CMD_reg <= 8'h0;
            VFIBi_BURST_reg <= 4'h0;
            VFIBi_SIZE_reg <= 4'h0;
            VFIBi_ADDR_reg <= NULL;
            VFIBi_DATA_reg <= NULL;
            count <= 16'b0;
        end
        else if (state == Ready & VFIBi_V) begin
            VFIBi_ID_reg <= VFIBi_ID;
            VFIBi_CMD_reg <= VFIBi_CMD;
            VFIBi_BURST_reg <= VFIBi_BURST;
            VFIBi_SIZE_reg <= VFIBi_SIZE;
            VFIBi_ADDR_reg <= VFIBi_ADDR;
            VFIBi_DATA_reg <= VFIBi_DATA;
            count <= |VFIBi_BURST ? (16'b1 << VFIBi_BURST) : 16'b0;
        end
        // burst read
        else if (VFIBi_CMD_reg == SEQR && |count) begin
            count <= count - 1;
            VFIBi_ADDR_reg <= VFIBi_ADDR_reg + (16'h1 << VFIBi_SIZE_reg);
        end
        // burst write
        else if (VFIBi_CMD_reg == SEQW && |count && VFIBi_V) begin
            count <= count - 1;
            VFIBi_ADDR_reg <= VFIBi_ADDR_reg + (16'h1 << VFIBi_SIZE_reg);
            VFIBi_DATA_reg <= VFIBi_DATA;
        end

        if (~VFIBi_ARST)
            VFIBo_V_reg <= 1'b0;
        else if (state != Ready & VFIBi_V_reg)
            VFIBo_V_reg <= 1'b1;
        else if (state == Ready | ~|count)
            VFIBo_V_reg <= 1'b0;
        else if (state == BWrite & ~VFIBi_V_reg)
            VFIBo_V_reg <= 1'b0;
    end

    // synchronized read & write
    always @(posedge VFIBi_CLK or negedge VFIBi_ARST) begin
        if (~VFIBi_ARST)
            VFIBo_DATA_reg <= NULL;
        else if (state == SRead || state == BRead)
            VFIBo_DATA_reg <= mem[VFIBi_ADDR_reg];
        else
            VFIBo_DATA_reg <= NULL;
        
        if (state == SWrite || (state == BWrite && VFIBi_V))
            mem[VFIBi_ADDR_reg] <= VFIBi_DATA_reg;
    end

    // finite state YSYX210152_Machine
    always @(*) begin
        case (state)
        Ready: begin
            if (VFIBi_V & VFIBi_CMD == SIGR)
                s_next = SRead;
            else if (VFIBi_V & VFIBi_CMD == SIGW)
                s_next = SWrite;
            else if (VFIBi_V & VFIBi_CMD == SEQR)
                s_next = BRead;
            else if (VFIBi_V & VFIBi_CMD == SEQW)
                s_next = BWrite;
            else
                s_next = Ready;
        end
        SRead: begin
            if (count == 0)
                s_next = Ready;
            else
                s_next = SRead;
        end
        SWrite: begin
            if (count == 0)
                s_next = Ready;
            else
                s_next = SWrite;
        end
        BRead: begin
            if (count == 0)
                s_next = Ready;
            else
                s_next = BRead;
        end
        BWrite: begin
            if (count == 0 || VFIBi_CMD == SEQE)
                s_next = Ready;
            else
                s_next = BWrite;
        end
        default:
            s_next = Ready;
        endcase
    end

    always @(posedge VFIBi_CLK or negedge VFIBi_ARST) begin
        if (~VFIBi_ARST)
            state <= Ready;
        else
            state <= s_next;
    end

endmodule
